<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Judge0 Dummy Client</title>
  <script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
</head>

<body style="padding: 0px 40px 40px 40px;">
  <h1>Judge0 Dummy Client</h1>
  <p>
    Judge0 dummy client can be used to try and test features of Judge0. Data is not validated on client side.
    Client first creates new submission and then fetches submission status every <code>1500ms</code>.
    Client stops fetching submission status after recieving an error or when submission status is not <code>In Queue</code> or <code>Processing</code>.
  </p>
  <p>
    Note that you need to define <strong>API URL</strong>. This is the base url of some Judge0 host (e.g. <code>https://api.judge0.com</code>).
    Description of network errors can be found in console of your browser, so check that for more information.
  </p>

  <hr>

  <strong>API URL</strong>&nbsp
  <input type="url" name="apiUrl" size="31" placeholder="https://api.judge0.com"><br><br>
  <strong>AUTHENTICATION HEADER</strong>&nbsp
  <input type="text" name="authnHeader" size="31" placeholder="X-Auth-Token" value="X-Auth-Token"><br><br>
  <strong>AUTHENTICATION TOKEN</strong>&nbsp
  <input type="text" size="31" name="authnToken"><br><br>
  <strong>AUTHORIZATION HEADER</strong>&nbsp
  <input type="text" name="authzHeader" size="31" placeholder="X-Auth-User" value="X-Auth-User"><br><br>
  <strong>AUTHORIZATION TOKEN</strong>&nbsp
  <input type="text" size="31" name="authzToken"><br><br>
  <strong>Source Code</strong>
  <input type="checkbox" name="sourceCodeIsNull"><code>null</code><br>
  <textarea name="sourceCode" rows="10" cols="50">
#include <stdio.h>

int main(void) {
  char name[10];
  scanf("%s", name);
  printf("hello, %s\n", name);
  return 0;
}</textarea><br><br>
  <strong>Language ID</strong>&nbsp
  <input type="text" name="languageId" value="50">
  <input type="checkbox" name="languageIdIsNull"><code>null</code><br><br>
  <strong>Number Of Runs</strong>&nbsp
  <input type="text" name="numberOfRuns" value="1">
  <input type="checkbox" name="numberOfRunsIsNull" checked><code>null</code><br><br>

  <strong>CPU Time Limit</strong>&nbsp
  <input type="text" name="cpuTimeLimit" value="5">
  <input type="checkbox" name="cpuTimeLimitIsNull" checked><code>null</code><br><br>

  <strong>CPU Extra Time</strong>&nbsp
  <input type="text" name="cpuExtraTime" value="1">
  <input type="checkbox" name="cpuExtraTimeIsNull" checked><code>null</code><br><br>

  <strong>Wall Time Limit</strong>&nbsp
  <input type="text" name="wallTimeLimit" value="10">
  <input type="checkbox" name="wallTimeLimitIsNull" checked><code>null</code><br><br>

  <strong>Memory Limit</strong>&nbsp
  <input type="text" name="memoryLimit" value="128000">
  <input type="checkbox" name="memoryLimitIsNull" checked><code>null</code><br><br>

  <strong>Stack Limit</strong>&nbsp
  <input type="text" name="stackLimit" value="64000">
  <input type="checkbox" name="stackLimitIsNull" checked><code>null</code><br><br>

  <strong>Max Processes And Or Threads</strong>&nbsp
  <input type="text" name="maxProcessesAndOrThreads" value="60">
  <input type="checkbox" name="maxProcessesAndOrThreadsIsNull" checked><code>null</code><br><br>

  <strong>Enable Per Process And Thread Time Limit</strong>
  <input type="radio" name="enablePerProcessAndThreadTimeLimit" value="true"> <code>true</code>
  <input type="radio" name="enablePerProcessAndThreadTimeLimit" value="false"> <code>false</code>
  <input type="radio" name="enablePerProcessAndThreadTimeLimit" value="null" checked> <code>null</code><br><br>

  <strong>Enable Per Process And Thread Memory Limit</strong>
  <input type="radio" name="enablePerProcessAndThreadMemoryLimit" value="true"> <code>true</code>
  <input type="radio" name="enablePerProcessAndThreadMemoryLimit" value="false"> <code>false</code>
  <input type="radio" name="enablePerProcessAndThreadMemoryLimit" value="null" checked> <code>null</code><br><br>

  <strong>Max File Size</strong>&nbsp
  <input type="text" name="maxFileSize" value="1024">
  <input type="checkbox" name="maxFileSizeIsNull" checked><code>null</code><br><br>

  <strong>Enable Network</strong>
  <input type="radio" name="enableNetwork" value="true"> <code>true</code>
  <input type="radio" name="enableNetwork" value="false"> <code>false</code>
  <input type="radio" name="enableNetwork" value="null" checked> <code>null</code><br><br>

  <strong>Stdin</strong>
  <input type="checkbox" name="stdinIsNull"><code>null</code><br>
  <textarea name="stdin" rows="5" cols="25">Judge0</textarea><br><br>

  <strong>Expected Output</strong>
  <input type="checkbox" name="expectedOutputIsNull" checked><code>null</code><br>
  <textarea name="expectedOutput" rows="5" cols="25"></textarea><br><br>

  <strong>Fields</strong>&nbsp
  <input type="text" size="50" name="fields"><br><br>

  <input type="checkbox" name="waitResponse">
  <strong>Wait for submission</strong><br><br>

  <input type="checkbox" name="base64EncodedRequest" checked>
  <strong>Send request with Base64 encoded data</strong><br><br>

  <input type="checkbox" name="base64EncodedResponse" checked>
  <strong>Accept response with Base64 encoded data</strong><br><br>

  <div id="panel" style="position: fixed; bottom: 0px; padding: 20px; background-color: #69DB7C; bottom: 20px; right: 20px; border: 1px solid #777">
    <button type="button" id="run">Run</button>
    <button type="button" id="stop" disabled>Stop</button>
    <button type="button" id="clearLog">Clear Log</button>
    <button type="button" id="backToTop">Back To Top</button>
  </div>

  <hr>

  <h2>Request/Response Log</h2><br>
  <pre id="log"></pre>

  <script>
    var stopFetch = false;

    if (window.location.protocol !== "file:") {
      $("input[name=apiUrl]").attr('value', window.location.origin);
    }

    function createQueryParameters(type = "Request") {
      var parameters = [];
      if ($(`input[name=base64Encoded${type}]`).is(":checked")) {
        parameters.push("base64_encoded=true");
      } else {
        parameters.push("base64_encoded=false");
      }

      var fields = $("input[name=fields]").val();
      if (fields.length != 0) {
        parameters.push(`fields=${fields}`);
      }

      var authnHeader = $("input[name=authnHeader]").val();
      var authnToken = $("input[name=authnToken]").val();
      if (authnToken.length != 0) {
        parameters.push(`${authnHeader}=${authnToken}`);
      }

      var authzHeader = $("input[name=authzHeader]").val();
      var authzToken = $("input[name=authzToken]").val();
      if (authzToken.length != 0) {
        parameters.push(`${authzHeader}=${authzToken}`);
      }

      if ($("input[name=waitResponse]").is(":checked")) {
        parameters.push("wait=true");
      }

      if (parameters.length == 0) {
        return "";
      }

      var queryParameters = "?";
      for (var i = 0; i < parameters.length - 1; i++) {
        queryParameters += parameters[i] + "&";
      }

      return queryParameters + parameters[parameters.length - 1];
    }

    function resetButtons() {
      stopFetch = false;
      $("#run").removeAttr("disabled");
      $("#stop").prop("disabled", true);
      $("#panel").css('background-color', '#69DB7C');
    }

    function appendToLog(text) {
      $("#log").text($("#log").text() + text + "\n");
      $('html, body').animate({
		      scrollTop: $("body")[0].scrollHeight
	    }, 500);
    }

    function handleError(jqXHR, textStatus, errorThrown) {
      appendToLog(`[Response ${new Date().toLocaleString()}] ${jqXHR.status} ${jqXHR.statusText}\n${JSON.stringify(jqXHR, null, 4)}\n`);
      appendToLog(`[DONE ${new Date().toLocaleString()}]\n\n\n`);
      resetButtons();
    }

    function fetchSubmission(apiUrl, token) {
      var queryParameters = createQueryParameters("Response");
      appendToLog(`[Request ${new Date().toLocaleString()}] GET ${apiUrl}/submissions/${token}${queryParameters}`);
      $.ajax({
        url: apiUrl + "/submissions/" + token + queryParameters,
        type: "GET",
        async: true,
        success: function(data, textStatus, jqXHR) {
          appendToLog(`[Response ${new Date().toLocaleString()}] ${jqXHR.status} ${jqXHR.statusText}\n${JSON.stringify(data, null, 4)}\n`);
          if ((data.status === undefined || data.status.id <= 2) && (data.status_id === undefined || data.status_id <= 2) && !stopFetch) {
            setTimeout(fetchSubmission.bind(null, apiUrl, token), 1500);
          } else if (!stopFetch) {
            appendToLog(`[DONE ${new Date().toLocaleString()}]\n\n\n`);
            resetButtons();
          } else {
            appendToLog(`[STOPPED ${new Date().toLocaleString()}]\n\n\n`);
            resetButtons();
          }
        },
        error: handleError
      });
    }

    $("#run").click(function() {
      $(this).prop("disabled", true);
      $("#stop").removeAttr("disabled");
      $("#panel").css('background-color', '#FF8787');

      var apiUrl = $("input[name=apiUrl]").val();
      var sourceCode = $("textarea[name=sourceCode]").val();
      var languageId = $("input[name=languageId]").val();
      var numberOfRuns = $("input[name=numberOfRuns]").val();
      var stdin = $("textarea[name=stdin]").val();
      var expectedOutput = $("textarea[name=expectedOutput]").val();
      var cpuTimeLimit = $("input[name=cpuTimeLimit]").val();
      var cpuExtraTime = $("input[name=cpuExtraTime]").val();
      var wallTimeLimit = $("input[name=wallTimeLimit]").val();
      var memoryLimit = $("input[name=memoryLimit]").val();
      var stackLimit = $("input[name=stackLimit]").val();
      var maxProcessesAndOrThreads = $("input[name=maxProcessesAndOrThreads]").val();
      var enablePerProcessAndThreadTimeLimit = $("input[name=enablePerProcessAndThreadTimeLimit]:checked").val() === "true";
      var enablePerProcessAndThreadMemoryLimit = $("input[name=enablePerProcessAndThreadMemoryLimit]:checked").val() === "true";
      var maxFileSize = $("input[name=maxFileSize]").val();
      var enableNetwork = $("input[name=enableNetwork]:checked").val() === "true";
      var wait = $("input[name=waitResponse]").is(":checked");

      var queryParameters = createQueryParameters();
      if ($("input[name=base64EncodedRequest]").is(":checked")) {
        sourceCode = btoa(sourceCode);
        stdin = btoa(stdin);
        expectedOutput = btoa(expectedOutput);
      }
      if ($("input[name=sourceCodeIsNull]").is(":checked")) {
        sourceCode = null;
      }
      if ($("input[name=languageIdIsNull]").is(":checked")) {
        languageId = null;
      }
      if ($("input[name=numberOfRunsIsNull]").is(":checked")) {
        numberOfRuns = null;
      }
      if ($("input[name=stdinIsNull]").is(":checked")) {
        stdin = null;
      }
      if ($("input[name=expectedOutputIsNull]").is(":checked")) {
        expectedOutput = null;
      }
      if ($("input[name=cpuTimeLimitIsNull]").is(":checked")) {
        cpuTimeLimit = null;
      }
      if ($("input[name=cpuExtraTimeIsNull]").is(":checked")) {
        cpuExtraTime = null;
      }
      if ($("input[name=wallTimeLimitIsNull]").is(":checked")) {
        wallTimeLimit = null;
      }
      if ($("input[name=memoryLimitIsNull]").is(":checked")) {
        memoryLimit = null;
      }
      if ($("input[name=stackLimitIsNull]").is(":checked")) {
        stackLimit = null;
      }
      if ($("input[name=maxProcessesAndOrThreadsIsNull]").is(":checked")) {
        maxProcessesAndOrThreads = null;
      }
      if ($("input[name=enablePerProcessAndThreadTimeLimit]:checked").val() === "null") {
        enablePerProcessAndThreadTimeLimit = null;
      }
      if ($("input[name=enablePerProcessAndThreadMemoryLimit]:checked").val() === "null") {
        enablePerProcessAndThreadMemoryLimit = null;
      }
      if ($("input[name=maxFileSizeIsNull]").is(":checked")) {
        maxFileSize = null;
      }
      if ($("input[name=enableNetwork]:checked").val() === "null") {
        enableNetwork = null;
      }

      var data = {
        source_code: sourceCode,
        language_id: languageId,
        number_of_runs: numberOfRuns,
        stdin: stdin,
        expected_output: expectedOutput,
        cpu_time_limit: cpuTimeLimit,
        cpu_extra_time: cpuExtraTime,
        wall_time_limit: wallTimeLimit,
        memory_limit: memoryLimit,
        stack_limit: stackLimit,
        max_processes_and_or_threads: maxProcessesAndOrThreads,
        enable_per_process_and_thread_time_limit: enablePerProcessAndThreadTimeLimit,
        enable_per_process_and_thread_memory_limit: enablePerProcessAndThreadMemoryLimit,
        max_file_size: maxFileSize,
        enable_network: enableNetwork
      };

      appendToLog(`[Request ${new Date().toLocaleString()}] POST ${apiUrl}/submissions${queryParameters}\n${JSON.stringify(data, null, 4)}`);

      $.ajax({
        url: apiUrl + "/submissions" + queryParameters,
        type: "POST",
        async: true,
        contentType: "application/json",
        data: JSON.stringify(data),
        success: function(data, textStatus, jqXHR) {
          appendToLog(`[Response ${new Date().toLocaleString()}] ${jqXHR.status} ${jqXHR.statusText}\n${JSON.stringify(data, null, 4)}\n`);
          if (!wait) {
            setTimeout(fetchSubmission.bind(null, apiUrl, data.token), 1500);
          } else {
            appendToLog(`[DONE ${new Date().toLocaleString()}]\n\n\n`);
            resetButtons();
          }
        },
        error: handleError
      });
    });

    $("#stop").click(function() {
      stopFetch = true;
    });

    $("#clearLog").click(function() {
      $("#log").html("");
    });

    $("#backToTop").click(function() {
      $('html, body').animate({
		      scrollTop: 0
	    }, 50);
    });
  </script>
</body>
</html>
